#include "allincludes.h"

//cout checked...

//OK, although the function Loop() looks so ugly, just edit your task here
int TaskManager::DoTask()
{
	//Edit here if you want the program do something else...
	//TestArg();
	LidarProTask();
	return SUCCEED;
}


TaskManager::TaskManager(struct LidarProArg arg, struct LidarINI ini)
{	/*
	taskStartTime=arg.startTimeSecInt;
	taskEndTime=arg.endTimeSecInt;
	averageMinutes=arg.averMinInt;
	taskSa=ini_Sa;
	taskLidarConst=ini_LidarConst;
	taskAtmosModelNum=ini_AtmosModel;
	inFilePath=newInFilePath;
	outFilePath=newOutFilePath;
	outputSize=ini_outputSize;
	*/
	bOneLineInitialed=false;

	memcpy(&taskArg,&arg,sizeof(LidarProArg));
	memcpy(&taskINI, &ini, sizeof(LidarINI));

	
	pool=new Container(MAXCONTAINERSIZE);  //if anything wrong happens, try to increase this const
	workshop=new Container(3);             //maybe 1 is enough, maybe 3 is safe...
	expo=new Container(1);
	//default output file
//	outFilePath="";
	//outFileName="DefaultOutputFile.txt";
	string tempInputPath(taskINI.inputPath);
	
	cout<<"***************************************************************"<<endl<<endl;
	string systemCommand;
	if(DIRorLS=="dir")
		systemCommand=DIRorLS+" "+tempInputPath+"*"+EXT+" /b >npFileList.lst";  //for Windows
	else
		systemCommand=DIRorLS+" "+tempInputPath+"*"+EXT+" >npFileList.lst"; //for Linux
	cout<<"  Sending System Command: "<<systemCommand<<endl<<endl;
	system(systemCommand.c_str());


	
	countI=0;
	countA=0;
	
	fileNameTime=Int2YYYYMMDDhhmmss(taskArg.startTimeSecInt)+"_"+Int2YYYYMMDDhhmmss(taskArg.endTimeSecInt);
	if(taskArg.b_v)
	{
		stringstream ss;
		string averMinStr;
		ss<<taskArg.averMinInt;
		ss>>averMinStr;
		fileNameTime=fileNameTime+"."+averMinStr+"min_average";
	}
	if(taskArg.b_i)
	{
		string fileNameI = taskINI.outputPath+fileNameTime+".backscat.txt";
		if(fileI.is_open())
			fileI.close();
		fileI.open(fileNameI.c_str());
		if(!fileI)
		{
			cout<<"Warning : Can't Create File: "<<fileNameI<<endl;
			cout<<"--Please check whether the outputPath Directory exists..."<<endl;
			taskArg.b_i=false;     //if file not created, just turn off that option...
		}
	}
	if(taskArg.b_a)
	{
		string fileNameA = taskINI.outputPath+fileNameTime+".extinctn.txt";
		if(fileA.is_open())
			fileA.close();
		fileA.open(fileNameA.c_str());
		if(!fileA)
		{
			cout<<"Warning : Can't Create File: "<<fileNameA<<endl;
			cout<<"--Please check whether the outputPath Directory exists..."<<endl;
			taskArg.b_a=false;     //if file not created, just turn off that option...
		}
	}
	if(taskArg.b_t)
	{
		stringstream ss;
		string heightStr;
		ss<<taskArg.lidarpro_height;
		ss>>heightStr;
		string fileNameT = taskINI.outputPath+fileNameTime+".extinctn_at_"+heightStr+"km.txt";
		if(fileT.is_open())
			fileT.close();
		fileT.open(fileNameT.c_str());
		if(!fileT)
		{
			cout<<"Warning : Can't Create File: "<<fileNameT<<endl;
			cout<<"--Please check whether the outputPath Directory exists..."<<endl;
			taskArg.b_t=false;     //if file not created, just turn off that option...
		}
		else if( taskArg.lidarpro_height>60.0*sin(taskINI.alpha*PI/180.0) || taskArg.lidarpro_height<0.0 )
		{
			cout<<"Warning : Can't output data at this height: "<<taskArg.lidarpro_height<<" km"<<endl;
			taskArg.b_t=false;
		}
	}
	if(taskArg.b_d)
	{
		stringstream ss;
		string limitHeightStr;
		ss<<taskArg.lidarpro_limit;
		ss>>limitHeightStr;
		string fileNameD = taskINI.outputPath+fileNameTime+".AOD_below_"+limitHeightStr+"m.txt";
		if(fileD.is_open())
			fileD.close();
		fileD.open(fileNameD.c_str());
		if(!fileD)
		{
			cout<<"Warning : Can't Create File: "<<fileNameD<<endl;
			cout<<"--Please check whether the outputPath Directory exists..."<<endl;
			taskArg.b_d=false;     //if file not created, just turn off that option...
		}
	}
	if(taskArg.b_c)
	{
		string fileNameC = taskINI.outputPath+fileNameTime+".CloudBottom.txt";
		if(fileC.is_open())
			fileC.close();
		fileC.open(fileNameC.c_str());
		if(!fileC)
		{
			cout<<"Warning : Can't Create File: "<<fileNameC<<endl;
			cout<<"--Please check whether the outputPath Directory exists..."<<endl;
			taskArg.b_c=false;     //if file not created, just turn off that option...
		}
	}
	if(taskArg.b_m)
	{
		string fileNameM = taskINI.outputPath+fileNameTime+".MixingLayerHeight.txt";
		if(fileM.is_open())
			fileM.close();
		fileM.open(fileNameM.c_str());
		if(!fileM)
		{
			cout<<"Warning : Can't Create File: "<<fileNameM<<endl;
			cout<<"--Please check whether the outputPath Directory exists..."<<endl;
			taskArg.b_m=false;     //if file not created, just turn off that option...
		}
	}

}


TaskManager::~TaskManager()
{
	cout<<"******************** Task Finished ***********************"<<endl;
	delete pool;
	delete workshop;
	delete expo;
	if(listFile.is_open())
		listFile.close();
	if(fileI.is_open())
		fileI.close();
	if(fileA.is_open())
		fileA.close();
	if(fileT.is_open())
		fileT.close();
	if(fileD.is_open())
		fileD.close();
	if(fileC.is_open())
		fileC.close();
	if(fileM.is_open())
		fileM.close();
}
int TaskManager::CloseAllFiles()
{
	if(listFile.is_open())
		listFile.close();
	if(fileI.is_open())
		fileI.close();
	if(fileA.is_open())
		fileA.close();
	if(fileT.is_open())
		fileT.close();
	if(fileD.is_open())
		fileD.close();
	if(fileC.is_open())
		fileC.close();
	if(fileM.is_open())
		fileM.close();
	return SUCCEED;
}
int TaskManager::TestArg()
{
	cout<<"======================================================"<<endl;
	cout<<workshop->lineList[0].GetTimeString()<<endl<<endl;

	cout<<Int2YYYYMMDDhhmmss(taskArg.startTimeSecInt)<<endl;
	cout<<Int2YYYYMMDDhhmmss(taskArg.endTimeSecInt)<<endl;
	cout<<taskArg.averMinInt;
	cout<<taskArg.lidarpro_height<<endl;
	cout<<taskArg.lidarpro_limit<<endl;

	cout<<taskINI.inputPath<<endl;
	cout<<taskINI.outputPath<<endl;


	if(taskArg.b_i)
		cout<<"Doing i"<<endl;
	if(taskArg.b_a)
		cout<<"Doing a"<<endl;
	if(taskArg.b_t)
		cout<<"Doing t"<<endl;
	if(taskArg.b_p)
		cout<<"Doing p"<<endl;
	if(taskArg.b_d)
		cout<<"Doing d"<<endl;
	if(taskArg.b_f)
		cout<<"Doing f"<<endl;
	if(taskArg.b_r)
		cout<<"Doing r"<<endl;
	if(taskArg.b_c)
		cout<<"Doing c"<<endl;
	if(taskArg.b_m)
		cout<<"Doing m"<<endl;
	if(taskArg.b_v)
		cout<<"Doing v"<<endl;



	return SUCCEED;
}


int TaskManager::LidarProTask()
{
	//OK, it seems no operation here would harm the workshop line...
	if(taskArg.b_Y && ( (!taskArg.b_a)||(!taskArg.b_i) ) )
	{
		workshop->lineList[0].WriteAsItIs("");
	}
	OneLine one=workshop->lineList[0];
	one.NormalizeData(taskINI.lidarConst);
	if(taskArg.b_i)
	{
		//cout<<"  Writing backscat data to img file ..."<<endl;
		one.WriteDataAsLidarPro(fileI,countI,taskINI.outputSize,taskArg.b_f, taskArg.b_G);
		if(taskArg.b_Y)
			one.WriteAsItIs();
	}
	if(taskArg.b_c)
	{
		cout<<"  Writing cloudbtm data to time serie file ..."<<endl;
		one.WriteCloudBottomTimeSerie(fileC);
	}
	if(taskArg.b_m)
	{
		cout<<"  Writing mixlayer data to time serie file ..."<<endl;
		one.WriteMixLayerTimeSerie(fileM);
	}
	if(taskArg.b_S)
	{
		cout<<endl<<endl<<endl;
		cout<<"===========   Making LIDAR back scattering ratio (s_a) Correction   ============="<<endl<<endl; 
		float newAOD;
		cout<<"  Please Enter The AOD of time:"<<one.GetTimeString()<<endl;
		cin>>newAOD;
		one.GetSa(taskINI, newAOD,one.HeightToIndex(taskINI.AODcalculationHeightLimit),"",10.0,50.0);   //need a parametre for AOD calculation height?
		cout<<endl<<"*********************************************** "<<endl<<endl;
	}
	if(taskArg.b_L)
	{
		cout<<endl<<endl<<endl;
		cout<<"===========   Making Lidar Constant Correction   ============="<<endl<<endl; 
		float newAOD, newLowHeight, newHighHeight;
		cout<<"  Please Enter The AOD of "<<one.GetTimeString()<<endl;
		cin>>newAOD;
		cout<<"  Please Enter The Low Height and High Height for the calculation (in km) :"<<endl;
		cin>>newLowHeight>>newHighHeight;
		float newLC=one.GetLidarConst(newAOD,newLowHeight, newHighHeight, true,"");
		cout<<endl;
	}
	if(taskArg.b_a||taskArg.b_t||taskArg.b_d)   //bug with d killed
	{
		int neededMaxHeightIndex=taskINI.outputSize+one.FIRSTDATAINDEX-1;
		int lidarproHeightIndex=one.HeightToIndex(taskArg.lidarpro_height);  //in lidarpro, the unit was metre..
		int lidarproLimitIndex=one.HeightToIndex(taskArg.lidarpro_limit);    //but i've converted it into km...

		if(taskArg.b_t && lidarproHeightIndex > neededMaxHeightIndex)
			neededMaxHeightIndex=lidarproHeightIndex;
		if(neededMaxHeightIndex>one.binsNum-1)
		{
			if(taskArg.b_t)
			{
				cout<<"Warning: Can't Get SigmaA at this Height: "<<taskArg.lidarpro_height<<endl;
				taskArg.b_t=false;
			}
			neededMaxHeightIndex=one.binsNum-1;
		}
		if(lidarproLimitIndex>one.binsNum-1)
		{
			lidarproLimitIndex=one.binsNum-1;
		}
		if(taskArg.b_Y)   //if you want to see the very detail data
		{
			neededMaxHeightIndex=one.binsNum-1;
		}
		OneLine sigmaALine=one.GetSigmaA(taskINI, neededMaxHeightIndex,taskINI.sa,taskINI.lidarConst);
		if(taskArg.b_a)
		{
			//cout<<"  Writing extinction data to img file ..."<<endl;
			sigmaALine.WriteDataAsLidarPro(fileA,countA,taskINI.outputSize,taskArg.b_f, taskArg.b_G);
		}
		if(taskArg.b_t)
		{
			sigmaALine.WriteSigmaATimeSerie(fileT,lidarproHeightIndex);
		}
		if(taskArg.b_d)
		{
			sigmaALine.WriteAODTimeSerie(taskINI, fileD, lidarproLimitIndex);
		}
		if(taskArg.b_Y)
			sigmaALine.WriteAsItIs();
	}

	if(taskArg.b_U)
	{
		workshop->lineList[0].WriteDataToAfterpulse("");		
	}
	if(taskArg.b_O)
	{
		cout<<endl<<endl<<endl;
		cout<<"===========   Making Overlap Coefficient   ============="<<endl<<endl; 
		float nearDistance, farDistance;
		cout<<"  Please Choose the Distance Region as the linear part of the curve (in km, e.g. 5.0 8.0 ): "<<endl;
		cin>>nearDistance>>farDistance;
		workshop->lineList[0].GetOverlapCoefficient(nearDistance,farDistance,""); //Notice: here we must use the unnormalised data
		
		//And it's best to put this at the end of all tasks because it'll change the workshop line..
		//Seems no need to put it at last now...
		cout<<endl<<"********************************"<<endl<<endl;
	}
	
	countI++;
	countA++;
	return SUCCEED;
}


//this function looks ugly, but it works...
//The key idea here is: search the files, \ 
//when a needed record is found, put it into the pool, 
//when the search goes out of a certain time period, flush the pool to MAKE an AVERAGED data
//and then work with the averaged data...
int TaskManager::Loop()     
{
	
	cout<<"Task Running ~~"<<endl;
	cout<<"From: "<<Int2YYYYMMDDhhmmss(taskArg.startTimeSecInt)<<" to "<<Int2YYYYMMDDhhmmss(taskArg.endTimeSecInt)<<" with "<<taskArg.averMinInt<<" minutes average"<<endl<<endl;

	listFile.open("npFileList.lst");
	if(!listFile)
	{
		cout<<"Error: no list find!"<<endl;
		exit(0);
	}
	string fileToHandleName;
	

	//these two indexs is used to find out whether a time is in a certain average period.
	//but it works only if the data is arranged in a normal time serie....
	int oldAverageIndex=0;
	int nowAverageIndex=0;

	pool->ClearAll();
	workshop->ClearAll();
	//knowing how to do average is so difficult
	while(!listFile.eof())
	{
		char fileNameChar[255];
		listFile.getline(fileNameChar,254);
		if(DIRorLS=="dir")
			fileToHandleName=taskINI.inputPath+string(fileNameChar); //for Windows...
		else
			fileToHandleName=string(fileNameChar);   //for Linux...
		
		//if you don't wanna use the file name to judge whether a file might contain the data you need , just delete the second item from the following line...
		if((fileToHandleName.length()-fileToHandleName.rfind(SLASH)>4) && (IsTheFileInTheTaskTimePeriod(fileToHandleName,taskArg.startTimeSecInt,taskArg.endTimeSecInt)) )  //this judgement is stupid?
		{			
			fReader.SwitchToFile(fileToHandleName);
			for(int i=0;i< fReader.fileDataBlockNum;i++)
			{
				fReader.ReadToLine(&tempLine,i);
				int tempTime=YYYYMMDDhhmmss2Int(
					tempLine.year,tempLine.month, tempLine.day, 
					tempLine.hours,tempLine.minutes, tempLine.seconds);
				if(tempTime>=taskArg.startTimeSecInt && tempTime <=taskArg.endTimeSecInt)
				{
					if(!bOneLineInitialed)   //Initialing OneLine's static members
					{
						cout<<endl<<" -------- Target Locked -------- "<<endl;
						tempLine.Initialize(taskINI, tempLine.binsLengthX);
						taskBinsLengthX=tempLine.binsLengthX;
						bOneLineInitialed=true;
					}
					else
					{   //if resolution changes... 
						if(tempLine.binsLengthX != this->taskBinsLengthX)
						{
							if(pool->usedSize > 0)
							{
								workshop->AddData(pool->Average(0,pool->usedSize-1));
//								workshop->lineList[0].WriteDetail(cout);
//The Key part: What will the Program do is decided by the following lines...				
								DoTask();
//End of Key part --------------------------------------------------------
								workshop->ClearAll();
								pool->ClearAll();
							}
							tempLine.Initialize(taskINI, tempLine.binsLengthX);
							taskBinsLengthX=tempLine.binsLengthX;
						}
					}
					if(taskArg.averMinInt<1)
					{
						workshop->AddData(tempLine);
//-------------Key Part-----------------------------------
						DoTask();
//------------End of Key Part--------------------------
						workshop->ClearAll();		
					}
					else
					{
						nowAverageIndex=(tempTime-taskArg.startTimeSecInt)/(taskArg.averMinInt*60);
						if(nowAverageIndex>oldAverageIndex)
						{
							oldAverageIndex=nowAverageIndex;
							if( pool->usedSize >0)
							{			
								workshop->AddData(pool->Average(0,pool->usedSize-1));
								
//The Key part -----------------------------------------
								DoTask();
//End of Key part --------------------------------------------------------
								workshop->ClearAll();
								pool->ClearAll();
							}
						}							
						pool->AddData(tempLine);		
					}
				}
			}
		}
	}
	if (pool->usedSize > 0)    //after all, if some data still remains in the pool, flush them...
	{
										
		workshop->AddData(pool->Average(0,pool->usedSize-1));
//The Key Part--------------------------------
		DoTask();
//End of KeyPart---------------------------
		pool->ClearAll();
		workshop->ClearAll();
	}
	return SUCCEED;
}


